﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RaiderManagerBackend
{
    public class Player
    {
        private List<PlayerPerformanceEntry> _performanceEntries = new List<PlayerPerformanceEntry>();
        private List<AttendanceEntry> _attendanceEntries = new List<AttendanceEntry>();
        private double _recentAveragePerformance;
        private double _totalAveragePerformance;
        private string _attendanceGrade;
        private string _performanceGrade;
        private string _officerGrade;
        private string _overallGrade;
        private string _name;
        private int _playerId = 0;
        private string _guildRank;

        private int _numLates;
        private int _numSignupAvailable;
        private int _numSignupUnavailable;
        private int _numNoShow;
        private int _numParticipated;
        private int _numBenched;

        #region Properties

        #region Entries

        public List<PlayerPerformanceEntry> PerformanceEntries
        {
            get { return _performanceEntries; }
        }

        public List<AttendanceEntry> AttendanceEntries
        {
            get { return _attendanceEntries; }
        }

        #endregion

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public int PlayerId
        {
            get { return _playerId; }
            set { _playerId = value; }
        }


        #region GradeProperties

        public string OverallGrade
        {
            get { return _overallGrade; }
            set { _overallGrade = value; }
        }

        public string OfficerGrade
        {
            get { return _officerGrade; }
            set { _officerGrade = value; }
        }

        public string PerformanceGrade
        {
            get { return _performanceGrade; }
            set { _performanceGrade = value; }
        }

        public string AttendanceGrade
        {
            get { return _attendanceGrade; }
            set { _attendanceGrade = value; }
        }

        #endregion

        #region Performance

        public double AverageRecentPerformance
        {
            get { return _recentAveragePerformance; }
            set { _recentAveragePerformance = value; }
        }

        public double AverageTotalPerformance
        {
            get { return _totalAveragePerformance; }
            set { _totalAveragePerformance = value; }
        }

        #endregion

        #region Attendance

        public int NumberOfLates
        {
            get { return _numLates; }
            private set { _numLates = value; }
        }

        public int NumberOfSignUpAvailable
        {
            get { return _numSignupAvailable; }
            private set { _numSignupAvailable = value; }
        }

        public int NumberOfSignupUnavailable
        {
            get { return _numSignupUnavailable; }
            private set { _numSignupUnavailable = value; }
        }

        public int NumberOfNoShows
        {
            get { return _numNoShow; }
            private set { _numNoShow = value; }
        }

        public int NumberOfParticipated
        {
            get { return _numParticipated; }
            private set { _numParticipated = value; }
        }

        public int NumberOfBenched
        {
            get { return _numBenched; }
            private set { _numBenched = value; }
        }

        #endregion

        public string GuildRank
        {
            get { return _guildRank; }
            set { _guildRank = value; }
        }

        #endregion

        public void SavePlayer()
        {
            SQLiteCommunicator comm = new SQLiteCommunicator();
            PlayerId = comm.AddUpdatePlayer(this);
        }

        public void CalculatePerformanceGrade()
        {
            double sumDifference = 0.0;
            int count = 0;

            foreach (PlayerPerformanceEntry perfEntry in this.PerformanceEntries)
            {
                sumDifference += perfEntry.DifferenceAsAPercentageOfMedian;
                if (++count == 5)
                {
                    AverageRecentPerformance = sumDifference / count;
                }
            }
            if (count != 0)
            {
                //calculate the total average
                AverageTotalPerformance = sumDifference / count;
                //If we didn't have enough performances to only calculate the first 5, recent = average.
                if (count < 5)
                {
                    AverageRecentPerformance = AverageTotalPerformance;
                }
                this.PerformanceGrade = BusinessLogic.GetGradeFromGradePoint(GetGradePointFromPerformance(this.AverageRecentPerformance));
            }
            else
            {
                this.PerformanceGrade = "none";
            }
        }

        public void CalculateAttendanceGrade()
        {
            //reset the numbers
            NumberOfBenched = 0;
            NumberOfLates = 0;
            NumberOfNoShows = 0;
            NumberOfParticipated = 0;
            NumberOfSignUpAvailable = 0;
            NumberOfSignupUnavailable = 0;

            int RecentAttendancePoints = 0;
            DateTime attendanceCutOff = DateTime.Now.Subtract(new TimeSpan(63, 0, 0, 0));
            foreach (AttendanceEntry aEntry in AttendanceEntries)
            {
                bool recent = aEntry.Raid.Date > attendanceCutOff;
                switch (aEntry.SignUpCode)
                {
                    case Enumerations.SignUpCode.Available:
                        if (recent)
                        {
                            RecentAttendancePoints += Enumerations.AttendancePoints.SignUpAvailable;
                        }
                        NumberOfSignUpAvailable++;
                        break;
                    case Enumerations.SignUpCode.Unavailable:
                        if (recent)
                        {
                            RecentAttendancePoints += Enumerations.AttendancePoints.SignUpUnavailable;
                        }
                        NumberOfSignupUnavailable++;
                        break;
                }

                switch (aEntry.AttendanceCode)
                {
                    case Enumerations.AttendanceCode.Benched:
                        if (recent)
                        {
                            RecentAttendancePoints += Enumerations.AttendancePoints.Benched;
                        }
                        NumberOfBenched++;
                        break;
                    case Enumerations.AttendanceCode.Late:
                        if (recent)
                        {
                            RecentAttendancePoints += Enumerations.AttendancePoints.Late;
                        }
                        NumberOfLates++;
                        break;
                    case Enumerations.AttendanceCode.NoShow:
                        if (recent)
                        {
                            RecentAttendancePoints += Enumerations.AttendancePoints.NoShow;
                        }
                        NumberOfNoShows++;
                        break;
                    case Enumerations.AttendanceCode.Participated:
                        if (recent)
                        {
                            RecentAttendancePoints += Enumerations.AttendancePoints.Participated;
                        }
                        NumberOfParticipated++;
                        break;
                }
            }

            this.AttendanceGrade = BusinessLogic.GetGradeFromGradePoint(GetGradePointFromAttendance(RecentAttendancePoints));
        }

        public void CalculateOverallGrade()
        {
            //set the weights we want to use for calculating the guild rank.
            double performanceGradeWeight = 1;
            double attendanceGradeWeight = 1;
            double officerGradeWeight = 1;
            int divisor = 3;
            if (OfficerGrade == (null) || OfficerGrade.Equals(string.Empty))
            {
                //if the Player doesn't have an officer grade, then we only have 2 entries in our average.
                divisor = 2;
                officerGradeWeight = 0;
            }

            //Get what is considered to be an A. We'll use this for calculating the weighted 
            double maxWeightedGradePoint = (performanceGradeWeight * 4 + attendanceGradeWeight * 4 + officerGradeWeight * 4) / divisor;

            double averageWeightedGradePoint = (PerformanceGrade.ToGradePoint() * performanceGradeWeight +
                                    AttendanceGrade.ToGradePoint() * attendanceGradeWeight +
                                    OfficerGrade.ToGradePoint() * officerGradeWeight) / divisor;

            double weightedPercentOfMaxGrade = averageWeightedGradePoint / maxWeightedGradePoint;
            OverallGrade = BusinessLogic.GetGradeFromGradePoint(weightedPercentOfMaxGrade * 4);
        }

        public void CalculateGuildRank()
        {
            
        }

        #region Private Methods
        private double GetGradePointFromPerformance(double x)
        {
            //these are pulled from pulling the trendline in excel and getting the formula.
            //We may want to pull these out and have them be editable in the program later.
            //71.616x^3 - 14.32x^2 + 4.313x + 2.0836
            double t1C = 71.616;
            double t2C = -14.32;
            double t3C = 4.313;
            double intercept = 2.0836;
            return t1C * (x*x*x) + t2C * (x*x) + t3C * x + intercept;
        }

        private double GetGradePointFromAttendance(int x)
        {
            //0.125x - 1
            double t1c = .125;
            double intercept = -1;
            return t1c * (x) + intercept;
        }
        #endregion
    }
}
